// $Id: proxy_array.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <algorithm>
#include <cstring>
#include <boost/test/unit_test.hpp>
#include <xpcom/array.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/proxy/array.hpp>

namespace xpcom {
namespace {

void do_in_impl_1(int* a, uint32_t s, uint32_t l)
{
    BOOST_CHECK(a == 0);
    BOOST_CHECK(s == 0);
    BOOST_CHECK(l == 0);
}

void do_in2_impl_1(int* a, uint32_t s)
{
    BOOST_CHECK(a == 0);
    BOOST_CHECK(s == 0);
}

void do_in_1(const array<int>& a)
{
    do_in_impl_1(proxy::array_in(a), proxy::array_in_size(a), proxy::array_in_length(a));
}

void do_in2_1(const array<int>& a)
{
    do_in2_impl_1(proxy::array_in(a), proxy::array_in_size2(a));
}

void do_in_impl_2(int* a, uint32_t s, uint32_t l)
{
    BOOST_CHECK(a != 0);
    BOOST_CHECK(l == 5);
    BOOST_CHECK(s >= l);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL(a[i], i);
    }
}

void do_in2_impl_2(int* a, uint32_t s)
{
    BOOST_CHECK(a != 0);
    BOOST_CHECK(s == 5);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL(a[i], i);
    }
}

void do_in_2(const array<int>& a)
{
    do_in_impl_2(proxy::array_in(a), proxy::array_in_size(a), proxy::array_in_length(a));
}

void do_in2_2(const array<int>& a)
{
    do_in2_impl_2(proxy::array_in(a), proxy::array_in_size2(a));
}

void do_in2_impl_3(const int* a, const int* b, const int* c, uint32_t s)
{
    BOOST_CHECK(a != 0 && b != 0 && c != 0);
    BOOST_CHECK(s == 5);
}

void do_in2_3(const array<int>& a, const array<int>& b, const array<int>& c)
{
    do_in2_impl_3(proxy::array_in(a), proxy::array_in(b), proxy::array_in(c),
            proxy::array_in_size2(a, b, c));
}

void do_out_impl_1(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = 0;
    *s = 0;
    *l = 0;
}

void do_out2_impl_1(int** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = 0;
    *s = 0;
}

void do_out_1(array<int>& a)
{
    do_out_impl_1(proxy::array_out(a), proxy::array_out_size(a), proxy::array_out_length(a));
}

void do_out2_1(array<int>& a)
{
    do_out2_impl_1(proxy::array_out(a), proxy::array_out_size2(a));
}

void do_out_impl_2(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(10);
    for (int i = 0; i < 10; ++i) {
        (*a)[i] = i;
    }
    *s = 10;
    *l = 6;
}

void do_out2_impl_2(int** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(10);
    for (int i = 0; i < 10; ++i) {
        (*a)[i] = i;
    }
    *s = 6;
}

void do_out_2(array<int>& a)
{
    do_out_impl_2(proxy::array_out(a), proxy::array_out_size(a), proxy::array_out_length(a));
}

void do_out2_2(array<int>& a)
{
    do_out2_impl_2(proxy::array_out(a), proxy::array_out_size2(a));
}

void do_out_impl_3(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(10);
    *s = 10;
    *l = 0;
}

void do_out2_impl_3(int** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(10);
    *s = 0;
}

void do_out_3(array<int>& a)
{
    do_out_impl_3(proxy::array_out(a), proxy::array_out_size(a), proxy::array_out_length(a));
}

void do_out2_3(array<int>& a)
{
    do_out2_impl_3(proxy::array_out(a), proxy::array_out_size2(a));
}

void do_inout_impl_1(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(10);
    for (int i = 0; i < 10; ++i) {
        (*a)[i] = i * 10;
    }
    *s = 10;
    *l = 4;
}

void do_inout2_impl_1(int** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(10);
    for (int i = 0; i < 10; ++i) {
        (*a)[i] = i * 10;
    }
    *s = 4;
}

void do_inout_1(array<int>& a)
{
    do_inout_impl_1(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_1(array<int>& a)
{
    do_inout2_impl_1(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_inout_impl_2(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(*s >= *l);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i], i * 10);
    }

    std::reverse((*a), (*a) + *l);
    *l = 4;
}

void do_inout2_impl_2(int** a, uint32_t* s)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*s == 5);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i], i * 10);
    }

    std::reverse((*a), (*a) + *s);
    *s = 4;
}

void do_inout_2(array<int>& a)
{
    do_inout_impl_2(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_2(array<int>& a)
{
    do_inout2_impl_2(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_inout_impl_3(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(*s >= *l);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i], i * 10);
    }

    memory::free(*a);
    *s = *l = 0;
    *a = memory::alloc<int>(20);
    for (int i = 0; i < 20; ++i) {
        (*a)[i] = i * 100;
    }
    *s = 20;
    *l = 18;
}

void do_inout2_impl_3(int** a, uint32_t* s)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*s == 5);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i], i * 10);
    }

    memory::free(*a);
    *s = 0;
    *a = memory::alloc<int>(20);
    for (int i = 0; i < 20; ++i) {
        (*a)[i] = i * 100;
    }
    *s = 18;
}

void do_inout_3(array<int>& a)
{
    do_inout_impl_3(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_3(array<int>& a)
{
    do_inout2_impl_3(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_inout_impl_4(int** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*l == 10);
    BOOST_CHECK(*s >= *l);
    for (int i = 0; i < 10; ++i) {
        BOOST_CHECK_EQUAL((*a)[i], i * 100);
    }

    memory::free(*a);
    *a = 0;
    *s = 0;
    *l = 0;
}

void do_inout2_impl_4(int** a, uint32_t* s)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*s == 10);
    for (int i = 0; i < 10; ++i) {
        BOOST_CHECK_EQUAL((*a)[i], i * 100);
    }

    memory::free(*a);
    *a = 0;
    *s = 0;
}

void do_inout_4(array<int>& a)
{
    do_inout_impl_4(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_4(array<int>& a)
{
    do_inout2_impl_4(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_out_isize2_impl_1(int** a, uint32_t n)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<int>(n);
    for (int i = 0; i < (int)n; ++i) {
        (*a)[i] = 8;
    }
}

void do_out_isize2_1(array<int>& a, uint32_t n)
{
    do_out_isize2_impl_1(proxy::array_out_isize2(a, n), n);
}

void do_out_isize2_impl_2(int** a, uint32_t n)
{
    BOOST_CHECK(*a == 0);
    *a = 0;
}

void do_out_isize2_2(array<int>& a, uint32_t n)
{
    do_out_isize2_impl_2(proxy::array_out_isize2(a, n), n);
}

}
}

BOOST_AUTO_TEST_CASE(test_proxy_array_in)
{
    using namespace xpcom;

    array<int> a1;
    array<int> a2;

    a2.reserve(10);
    a2.resize(5);
    for (int i = 0; i < 5; ++i) {
        a2[i] = i;
    }

    do_in_1(a1);
    do_in_2(a2);
}

BOOST_AUTO_TEST_CASE(test_proxy_array_in2)
{
    using namespace xpcom;

    array<int> a1;
    array<int> a2;
    array<int> a3(20);

    a2.reserve(10);
    a2.resize(5);
    for (int i = 0; i < 5; ++i) {
        a2[i] = i;
    }

    do_in2_1(a1);
    do_in2_2(a2);

    a1.resize(5);
    a2.resize(50);
    do_in2_3(a1, a2, a3);
}

BOOST_AUTO_TEST_CASE(test_proxy_array_out)
{
    using namespace xpcom;

    array<int> a(9);

    do_out_1(a);
    BOOST_CHECK(a.empty());

    do_out_2(a);
    BOOST_CHECK(a.size() == 6);
#ifdef XPCOM_ENABLE_LENGTH_IS
    BOOST_CHECK(a.capacity() == 10);
#endif
    for (int i = 0; i < 6; ++i) {
        BOOST_CHECK_EQUAL(a[i], i);
    }

    do_out_3(a);
    BOOST_CHECK(a.empty());
#ifdef XPCOM_ENABLE_LENGTH_IS
    BOOST_CHECK(a.capacity() == 10);
#endif
}

BOOST_AUTO_TEST_CASE(test_proxy_array_out2)
{
    using namespace xpcom;

    array<int> a(9);

    do_out2_1(a);
    BOOST_CHECK(a.empty());

    do_out2_2(a);
    BOOST_CHECK(a.size() == 6);
    for (int i = 0; i < 6; ++i) {
        BOOST_CHECK_EQUAL(a[i], i);
    }

    do_out2_3(a);
    BOOST_CHECK(a.empty());
}

BOOST_AUTO_TEST_CASE(test_proxy_array_inout)
{
    using namespace xpcom;

    array<int> a;

    do_inout_1(a);
    BOOST_CHECK(a.size() == 4);
#ifdef XPCOM_ENABLE_LENGTH_IS
    BOOST_CHECK(a.capacity() == 10);
#endif
    for (int i = 0; i < 4; ++i) {
        BOOST_CHECK_EQUAL(a[i], i * 10);
    }

    a.resize(5);
    a[4] = 40;
    do_inout_2(a);
    BOOST_CHECK(a.size() == 4);
    BOOST_CHECK_EQUAL(a[0], 40);
    BOOST_CHECK_EQUAL(a[1], 30);
    BOOST_CHECK_EQUAL(a[2], 20);
    BOOST_CHECK_EQUAL(a[3], 10);

    a.resize(5);
    for (int i = 0; i < 5; ++i) {
        a[i] = i * 10;
    }
    do_inout_3(a);
    BOOST_CHECK(a.size() == 18);
#ifdef XPCOM_ENABLE_LENGTH_IS
    BOOST_CHECK(a.capacity() == 20);
#endif
    for (int i = 0; i < 18; ++i) {
        BOOST_CHECK_EQUAL(a[i], i * 100);
    }

    a.resize(10);
    do_inout_4(a);
    BOOST_CHECK(a.empty());
}

BOOST_AUTO_TEST_CASE(test_proxy_array_inout2)
{
    using namespace xpcom;

    array<int> a;

    do_inout2_1(a);
    BOOST_CHECK(a.size() == 4);
    for (int i = 0; i < 4; ++i) {
        BOOST_CHECK_EQUAL(a[i], i * 10);
    }

    a.resize(5);
    a[4] = 40;
    do_inout2_2(a);
    BOOST_CHECK(a.size() == 4);
    BOOST_CHECK_EQUAL(a[0], 40);
    BOOST_CHECK_EQUAL(a[1], 30);
    BOOST_CHECK_EQUAL(a[2], 20);
    BOOST_CHECK_EQUAL(a[3], 10);

    a.resize(5);
    for (int i = 0; i < 5; ++i) {
        a[i] = i * 10;
    }
    do_inout2_3(a);
    BOOST_CHECK(a.size() == 18);
    for (int i = 0; i < 18; ++i) {
        BOOST_CHECK_EQUAL(a[i], i * 100);
    }

    a.resize(10);
    do_inout2_4(a);
    BOOST_CHECK(a.empty());
}

BOOST_AUTO_TEST_CASE(test_proxy_array_out_isize2)
{
    using namespace xpcom;

    array<int> a(1);

    do_out_isize2_1(a, 8);
    BOOST_CHECK(a.size() == 8);
    for (int i = 0; i < 8; ++i) {
        BOOST_CHECK_EQUAL(a[i], 8);
    }

    do_out_isize2_1(a, 0);
    BOOST_CHECK(a.empty());

    do_out_isize2_2(a, 5);
    BOOST_CHECK(a.empty());

    do_out_isize2_2(a, 0);
    BOOST_CHECK(a.empty());
}

